package com.zytech.zspider.task.impl.http;

import com.google.common.base.Charsets;
import com.google.common.collect.Maps;
import com.zytech.modules.lang.AggregateFunction;
import com.zytech.modules.lang.Function;
import com.zytech.modules.lang.Langs;
import com.zytech.modules.lang.Strings;
import com.zytech.modules.lang.annotations.Stateless;
import com.zytech.zspider.core.TaskDefinition;
import com.zytech.zspider.core.TaskDefinitionHelper;
import com.zytech.zspider.core.TaskExceptions;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Map;

/**
 * @author siuming
 */
@Stateless
class HttpClientTaskRequestExecutor implements HttpTaskRequestExecutor {

    @Override
    public HttpTaskResponse submit(HttpTaskRequest taskreq, Map<String, String> settings) {
        HttpEntity entity = null;
        try {

            HttpUriRequest req = transformHttpRequest(taskreq, settings);
            HttpResponse resp = HttpClientFactory.of(settings).execute(req);

            Map<String, String> headers = Langs.aggregate(Maps.<String, String>newHashMap(), Arrays.asList(resp.getAllHeaders()), new AggregateFunction<Map<String, String>, Header>() {
                @Override
                public Map<String, String> apply(Map<String, String> result, Header header) {
                    result.put(header.getName(), header.getValue());
                    return result;
                }
            });

            entity = resp.getEntity();
            if (null == entity) {
                return HttpTaskResponse.ofBuilder().headers(headers).build();
            }

            ContentType contentType = ContentType.get(entity);
            byte[] content = EntityUtils.toByteArray(entity);
            if (null == contentType) {
                return HttpTaskResponse.ofBuilder().headers(headers).content(content).build();
            }

            return HttpTaskResponse
                    .ofBuilder()
                    .headers(headers)
                    .content(content)
                    .mimeType(contentType.getMimeType())
                    .charset(Langs.nullSafe(contentType.getCharset(), Charsets.UTF_8).name())
                    .build();
        } catch (IOException e) {
            throw TaskExceptions.transform(e);
        } finally {
            EntityUtils.consumeQuietly(entity);
        }
    }

    private HttpUriRequest transformHttpRequest(HttpTaskRequest request, Map<String, String> props) throws UnsupportedEncodingException {

        String encoding = TaskDefinitionHelper.getSetting(props, TaskDefinition.Settings.HTTP_ENCODING, Charsets.UTF_8.name());
        HttpUriRequest httpUriRequest = null;
        switch (request.getVerb()) {
            case GET:
                httpUriRequest = new HttpGet(HttpTaskRequestUrlBuilder.buildQueryUrl(request.getUrl(), encoding, request.getParams()));
                break;
            case POST:
                HttpPost post = new HttpPost(request.getUrl());
                if (!Strings.isBlank(request.getBody())) {
                    post.setEntity(new ByteArrayEntity(request.getBody().getBytes(Charset.forName(encoding))));
                }
                if (!request.getParams().isEmpty() && null == post.getEntity()) {
                    Iterable<NameValuePair> pairs = Langs.map(request.getParams().entrySet(), new Function<Map.Entry<String, String>, NameValuePair>() {
                        @Override
                        public NameValuePair apply(Map.Entry<String, String> entry) {
                            return new BasicNameValuePair(entry.getKey(), entry.getValue());
                        }
                    });
                    post.setEntity(new UrlEncodedFormEntity(pairs, Charset.forName(encoding)));
                }
                httpUriRequest = post;
                break;
            default:
                throw new UnsupportedOperationException(String.format("Verb[%s] current is unsupported.", request.getVerb()));
        }

        for (Map.Entry<String, String> header : request.getHeaders().entrySet()) {
            httpUriRequest.setHeader(header.getKey(), header.getValue());
        }
        return httpUriRequest;
    }

    @Override
    public void shutdown() {
        // TODO
    }
}
